package server;


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import server.config.NettyConfig;
import server.request.RequestProcessor;
import server.servlet.HttpServlet;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * @author cg
 * @date 2023/6/7 13:17
 */
public class BootStrap {
    //定义端口号
    private int port = 8080;

    private final String webConfig = "web.xml";

    private final String parameterConfig = "tomcat.properties";

    private Class<?> config;

    /**
     * 保存url和servlet的请求映射关系
     */
    private static Map<String, HttpServlet> httpServletMap = new HashMap<>();

    public BootStrap() {
        config = this.getClass();
    }

    public BootStrap(Class<?> config) {
        this.config = config;
    }

    public static void main(String[] args) {
        BootStrap bootStrap = new BootStrap();
        bootStrap.start();
    }

    public void start() {
        //初始化servlet
        loadServlet();
        //初始化server配置
        loadServer();
        NettyConfig nettyConfig = new NettyConfig(parameterConfig);
        // 监听线程
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        //工作线程
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap().group(bossGroup, workGroup)//添加线程组
                    .channel(NioServerSocketChannel.class)
                    //添加处理流程
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // HttpResponseEncoder 编码器
                            socketChannel.pipeline().addLast(new HttpResponseEncoder());
                            // HttpRequestDecoder 解码器
                            socketChannel.pipeline().addLast(new HttpRequestDecoder());
                            // 业务逻辑处理
                            socketChannel.pipeline().addLast(new RequestProcessor(httpServletMap));
                        }
                    })
                    // 针对主线程的配置 分配线程最大数量 128
                    .option(ChannelOption.SO_BACKLOG, 128)
                    // 针对子线程的配置 保持长连接
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
            ChannelFuture future = bootstrap.bind(port).syncUninterruptibly();
            System.out.println("AIO服务器启动，绑定" + port + "端口");
            //异步关闭
            future.channel().closeFuture().syncUninterruptibly();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }


    /**
     * 初始化server配置
     */
    private void loadServer() {
        String prefix = "server.";
        Properties properties = new Properties();
        try {
            properties.load(this.getClass().getClassLoader().getResourceAsStream(parameterConfig));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String port = properties.getProperty(prefix + "port");
        this.port = port == null ? 8080 : Integer.parseInt(port);
    }

    /**
     * 解析xml中的请求路径，并映射到对应的servlet类中
     */
    private void loadServlet() {
        //加载web.xml文件
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(webConfig);
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(inputStream);
            //获取根元素
            Element rootElement = document.getRootElement();
            //找到所有servlet标签
            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (Element selectNode : selectNodes) {
                //解析<servlet-name>
                String servletName = selectNode.selectSingleNode("servlet-name").getStringValue();
                //解析<servlet-class>
                String servletClass = selectNode.selectSingleNode("servlet-class").getStringValue();

                //2, 根据servlet-name找到<servlet-mapping>中与其匹配的<url-pattern>
                //Xpath表达式：从/web-app/servlet-mapping下查询，查询出<servlet-name=servletName>的元素
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']'");
                String url = servletMapping.selectSingleNode("url-pattern").getStringValue();
                Class<?> clazz = Class.forName(servletClass);
                Object instance = clazz.newInstance();
                httpServletMap.put(url, (HttpServlet) instance);
                //执行初始化操作
                Method initMethod = clazz.getDeclaredMethod("init", Class.class);
                initMethod.setAccessible(true);
                initMethod.invoke(instance, config);
            }
        } catch (NoSuchMethodException ignored) {
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (inputStream != null) inputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

}
